package com.sakiko.audiobook.album.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sakiko.audiobook.album.mapper.AlbumAttributeValueMapper;
import com.sakiko.audiobook.album.mapper.AlbumInfoMapper;
import com.sakiko.audiobook.album.mapper.AlbumStatMapper;
import com.sakiko.audiobook.album.service.AlbumInfoService;
import com.sakiko.audiobook.common.cache.SakiCache;
import com.sakiko.audiobook.common.constant.RedisConstant;
import com.sakiko.audiobook.common.constant.SystemConstant;
import com.sakiko.audiobook.common.rabbit.constant.MqConst;
import com.sakiko.audiobook.common.rabbit.service.RabbitService;
import com.sakiko.audiobook.common.result.Result;
import com.sakiko.audiobook.model.album.AlbumAttributeValue;
import com.sakiko.audiobook.model.album.AlbumInfo;
import com.sakiko.audiobook.model.album.AlbumStat;
import com.sakiko.audiobook.query.album.AlbumInfoQuery;
import com.sakiko.audiobook.vo.album.AlbumAttributeValueVo;
import com.sakiko.audiobook.vo.album.AlbumInfoVo;
import com.sakiko.audiobook.vo.album.AlbumListVo;
import com.sakiko.audiobook.vo.album.AlbumStatVo;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private AlbumStatMapper albumStatMapper;

    @Autowired
    private AlbumAttributeValueMapper albumAttributeValueMapper;

    @Autowired
    private RabbitService rabbitService; // 生产环境应该先发平台审核，在上架到索引库

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 专辑分页查询列表
     *
     * @param albumListVOPage
     * @param albumInfoQuery
     * @return
     */
    @Override
    public IPage<AlbumListVo> findUserAlbumPage(Page<AlbumListVo> albumListVOPage, AlbumInfoQuery albumInfoQuery) {
        // album_info; album_stat;
        // 使用 mapper层调用 sql语句
        return albumInfoMapper.selectUserAlbumPage(albumListVOPage, albumInfoQuery);
    }

    /**
     * 删除专辑信息
     *
     * @param albumId
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 一次性操作多张表 + 事务
    public void removeAlbumInfo(Long albumId) {
        // 通过专辑 id获取专辑对象
        AlbumInfo albumInfo = this.getById(albumId);
        // 判断专辑对象是否存在
        if (null == albumInfo)
            return; // 也可以抛异常

        // todo: album_info:
        this.removeById(albumId);

        // todo: album_stat:
        // delete from album_stat where album_id = ?
        // Wrapper构建删除条件: 泛型<T> -- 操作的表对应的实体类
        albumStatMapper.delete(new LambdaQueryWrapper<AlbumStat>().eq(AlbumStat::getAlbumId, albumId));

        // todo: album_attribute_value
        albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, albumId));

        // 下架
        rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, albumInfo.getId());
    }

    /**
     * 根据专辑 id查询专辑信息
     *
     * @param albumId
     * @return
     */
    @Override
    @SakiCache(prefix = RedisConstant.ALBUM_INFO_PREFIX) // album:info:
    public AlbumInfo getAlbumInfo(Long albumId) {
        return getAlbumInfoDB(albumId);
//        // 查询数据库之前，先查询缓存; 缓存没有查询数据库！ 查询数据库之前 +锁 ==> redisson
//        // 不涉及修改 使用 string 类型就可以，不用使用 hash(使用 hash主要是为了修改时，可以只选取某一 field对应的 value进行序列化)
//        String skuKey = RedisConstant.ALBUM_INFO_PREFIX + albumId;
//        // 利用缓存查询数据
//        try {
//            AlbumInfo albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(skuKey);
//            if (null == albumInfo) {
//                // 缓存中不存在，使用 redisson框架获取锁
//                String lockKey = RedisConstant.ALBUM_LOCK_SUFFIX + albumId;
//                RLock lock = redissonClient.getLock(lockKey); // 获取锁的 "资格"
//                // 锁之前，在检查下 (redisson锁有重试机制)
//                albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(skuKey);
//                if (null != albumInfo) {
//                    return albumInfo; // 缓存此时有数据
//                }
//                // 上锁
//                lock.lock(); // 连接 redis服务器、尝试上锁
//                try {
//                    // 找数据库
//                    albumInfo = this.getAlbumInfoDB(albumId);
//                    // 判断数据库中数据是否存在
//                    if (null == albumInfo) {
//                        // 数据库中也不存在，缓存中存储一个空对象
//                        redisTemplate.opsForValue().set(skuKey, new AlbumInfo(), RedisConstant.ALBUM_TEMPORARY_TIMEOUT, TimeUnit.SECONDS);
//                        return new AlbumInfo();
//                    } else {
//                        // 数据库中存在
//                        redisTemplate.opsForValue().set(skuKey, albumInfo, RedisConstant.ALBUM_TIMEOUT, TimeUnit.SECONDS);
//                        return albumInfo;
//                    }
//                } catch (Exception e) {
//                    log.error("AlbumInfoServiceImpl.getAlbumInfo::查询数据库出错" + e.getMessage());
//                } finally {
//                    // 解锁
//                    lock.unlock();
//                }
//            } else {
//                // 缓存中有数据
//                return albumInfo;
//            }
//        } catch (RuntimeException e) {
//            // 发送预警邮件、钉钉...
//            log.error("AlbumInfoServiceImpl.getAlbumInfo获取数据异常！" + e.getMessage());
//        }
//        // 查询数据库
//        return getAlbumInfoDB(albumId);
    }

    @Nullable
    private AlbumInfo getAlbumInfoDB(Long albumId) {
        // 获取专辑对象 album_info
        AlbumInfo albumInfo = this.getById(albumId);
        // 获取 album_attribute_value
        if (null != albumInfo) {
            // 利用 mapper查询
            List<AlbumAttributeValue> albumAttributeValueList =
                    albumAttributeValueMapper.selectList(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, albumId));
            // 存储属性与属性值集合
            albumInfo.setAlbumAttributeValueVoList(albumAttributeValueList);
        }
        return albumInfo;
    }

    /**
     * 修改专辑信息
     *
     * @param albumId
     * @param albumInfoVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAlbumInfo(Long albumId, AlbumInfoVo albumInfoVo) {
        // todo: album_info
        AlbumInfo albumInfo = new AlbumInfo();
        // 赋值
        BeanUtils.copyProperties(albumInfoVo, albumInfo);
        albumInfo.setId(albumId);
        albumInfoMapper.updateById(albumInfo); // == this.updateById();
        // todo: album_attribute_value (删除原有的，然后对新数据保存)
        albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, albumId));
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        if (!CollectionUtils.isEmpty(albumAttributeValueVoList)) {
            // 循环遍历保存数据
            for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
                // 创建对象
                AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();

                // 其余属性 属性拷贝即可
                BeanUtils.copyProperties(albumAttributeValueVo, albumAttributeValue);
                // 赋值 专辑ID
                albumAttributeValue.setAlbumId(albumInfo.getId()); // 前端 AlbumAttributeValueVoList没带 albumId
                // 循环插入 insert
                albumAttributeValueMapper.insert(albumAttributeValue);
            }
        }
        // 判断 isOpen = 0 or 1
        if ("1".equals(albumInfoVo.getIsOpen())) {
            // 上架; 发送的内容由消费者决定。
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumId);
            // 第二步: 向布隆过滤器添加数据，真实生产环境下，应该在审核之后
//            RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
//            bloomFilter.add(albumId);
        } else {
            // 下架
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, albumId);
        }
    }

    /**
     * 获取用户所有的专辑列表
     *
     * @return
     */
    @Override
    public List<AlbumInfo> findUserAllAlbumList() {
        // select * from album_info where user_id = ? and is_deleted = 0;
        // 最好有分页功能 (懒加载
        Page<AlbumInfo> albumInfoPage = new Page<>(1, 20);
//        List<AlbumInfo> albumInfoList = albumInfoMapper.selectList(null);
        Page<AlbumInfo> infoPage =
                albumInfoMapper.selectPage(albumInfoPage, new LambdaQueryWrapper<AlbumInfo>().orderByDesc(AlbumInfo::getId));
        return infoPage.getRecords();
    }

    @Override
    @SakiCache(prefix = RedisConstant.ALBUM_STAT_PREFIX) // album:stat:
    public AlbumStatVo getAlbumStatVo(Long albumId) {
        // 调用 mapper
        return albumInfoMapper.selectAlbumStatVo(albumId);
    }

    /**
     * 保存专辑信息
     *
     * @param albumInfoVo
     * @param userId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveAlbumInfo(AlbumInfoVo albumInfoVo, Long userId) { // 数据源 albumInfoVO
        // 向表中插入数据; album_info album_stat album_attribute_value
        // todo: album_info:
        // 创建对象
        AlbumInfo albumInfo = new AlbumInfo();
        // 给对象赋值，属性拷贝
        BeanUtils.copyProperties(albumInfoVo, albumInfo);
        // userId 未赋值
        albumInfo.setUserId(userId);
        // tracks_for_free 免费试听的集数 ==> vip免费或付费情况下
        if (!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(albumInfoVo.getPayType())) {
            // 需要设置免费集数; 暂时给一个默认值
            albumInfo.setTracksForFree(5);
        }
        // 专辑状态 (这里直接设置了通过) 但是真实环境下，专辑的状态应该由平台审核是否合法！
        albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
        // 保存数据   @TableId(type = IdType.AUTO) ==> 主键回显
        albumInfoMapper.insert(albumInfo);

        // todo: album_stat:
        this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_PLAY); // 0401 播放量
        this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_SUBSCRIBE); // 0402 订阅量
        this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_BROWSE); // 0403 购买量
        this.saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_COMMENT); // 0404 评论量

        // todo: album_attribute_value
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        if (!CollectionUtils.isEmpty(albumAttributeValueVoList)) {
            // 循环遍历保存数据
            for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
                // 创建对象
                AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();

                // 其余属性 属性拷贝即可
                BeanUtils.copyProperties(albumAttributeValueVo, albumAttributeValue);
                // 赋值 专辑ID
                albumAttributeValue.setAlbumId(albumInfo.getId()); // 前端 AlbumAttributeValueVoList没带 albumId
                // 循环插入 insert ==> 可以改成批量插入数据
                albumAttributeValueMapper.insert(albumAttributeValue);
            }
        }
        // 判断 isOpen = 0 or 1
        if ("1".equals(albumInfoVo.getIsOpen())) {
            // 上架; 发送的内容由消费者决定。
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId());
            // 第二步: 向布隆过滤器添加数据，真实生产环境下，应该在审核之后
//            RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
//            bloomFilter.add(albumInfo.getId());
        }
    }

    private void saveAlbumStat(Long albumId, String statPlay) {
        // 创建对象
        AlbumStat albumStat = new AlbumStat();
        // 赋值
        albumStat.setAlbumId(albumId);
        albumStat.setStatType(statPlay); // 类型
        albumStat.setStatNum(new Random().nextInt(10000)); // 量，例如: 订阅量等... random搞个好看点的数据
        albumStatMapper.insert(albumStat);
    }
}
